package darknet.application.chat.impl.net;

import darknet.core.spec.net.Message;
import darknet.core.spec.net.ProtocolSender;
import darknet.core.spec.net.Response;
import darknet.core.spec.net.StreamableMessage;

import darknet.application.chat.impl.net.response.ListChannelResponse;
import darknet.application.chat.impl.net.message.SessionExitMessage;
import darknet.application.chat.impl.net.message.BroadcastExitChannelMessage;
import darknet.application.chat.impl.net.message.BroadcastChannelMessage;
import darknet.application.chat.impl.net.message.ListChannelMessage;
import darknet.application.chat.impl.net.message.SessionStartMessage;
import darknet.application.chat.impl.net.message.PrivateMessage;
import darknet.application.chat.impl.net.message.ExitChannelMessage;
import darknet.application.chat.impl.net.message.ChannelMessage;
import darknet.application.chat.impl.net.message.JoinChannelMessage;
import darknet.application.chat.impl.net.message.BroadcastJoinChannelMessage;
import darknet.application.chat.impl.net.message.CloseChannelMessage;
import darknet.application.chat.impl.net.response.JoinChannelResponse;

import darknet.core.impl.net.OKResponse;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;

/**
 * Implementation of a chat protocol sender.
 * 
 * @author Yoyo
 */
public class ChatProtocolSender implements ProtocolSender {

    /** Chat protocol */
    private ChatProtocol protocol = ChatProtocol.getCurrent();

    /**
     * Initialise a new chat protocol sender.
     */
    public ChatProtocolSender() {}

    @Override
    public boolean canSend(Message toSend) {

        if (toSend == null) {
            throw new IllegalArgumentException("toSend is null");
        }

        return protocol.equals(toSend.getHeader().getProtocol());
    }

    @Override
    public Response send(StreamableMessage toSend) {

        if (toSend == null) {
            throw new IllegalArgumentException("toSend is null");
        }

        // Send the message.
        Response response = sendMessage(toSend);

        return response;
    }

    private Response sendMessage(StreamableMessage toSend) {

        Response response = null;

        if (toSend.getWrapped() instanceof PrivateMessage) {

            response = sendPrivateMessage(toSend);

        } else if (toSend.getWrapped() instanceof ChannelMessage) {

            response = sendChannelMessage(toSend);

        } else if (toSend.getWrapped() instanceof BroadcastChannelMessage) {

            response = sendBroadcastMessage(toSend);

        } else if (toSend.getWrapped() instanceof ExitChannelMessage) {

            response = sendExitChannelMessage(toSend);

        } else if(toSend.getWrapped() instanceof BroadcastExitChannelMessage) {

            response = sendBroadcastExitChannelMessage(toSend);

        } else if (toSend.getWrapped() instanceof ListChannelMessage) {

            response = sendListChannelMessage(toSend);

        } else if (toSend.getWrapped() instanceof SessionStartMessage) {
            
            response = sendSessionStartMessage(toSend);

        } else if (toSend.getWrapped() instanceof SessionExitMessage) {

            response = sendSessionExitMessage(toSend);

        } else if (toSend.getWrapped() instanceof CloseChannelMessage) {

            response = sendCloseChannelMessage(toSend);
            
        } else if (toSend.getWrapped() instanceof JoinChannelMessage) {

            response = sendJoinChannelMessage(toSend);
            
        } else if (toSend.getWrapped() instanceof BroadcastJoinChannelMessage) {

            response = sendBroadcastJoinChannelMessage(toSend);
        }

        return response;

    }

    private OKResponse sendSessionStartMessage(StreamableMessage toSend) {

        OKResponse response = null;

        try {

            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }

    /**
     * Send the private message.
     *
     * @param toSend The message to send.
     * @return
     */
    private OKResponse sendPrivateMessage(StreamableMessage toSend) {

        OKResponse response = null;

        try {

            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
        return response;
    }

    private OKResponse sendSessionExitMessage(StreamableMessage toSend) {

        OKResponse response = null;

        try {

            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }

    private ListChannelResponse sendListChannelMessage(StreamableMessage toSend) {

        ListChannelResponse response = null;

        try {

            ObjectOutputStream out = new ObjectOutputStream(toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();

            ObjectInputStream in = new ObjectInputStream(toSend.getInput());
            response = (ListChannelResponse) in.readObject();
            in.close();

        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(ex);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }

    private JoinChannelResponse sendJoinChannelMessage(StreamableMessage toSend) {

        JoinChannelResponse response = null;

        try {
            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();

            ObjectInputStream in = new ObjectInputStream(toSend.getInput());
            response = (JoinChannelResponse) in.readObject();
            in.close();

        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(ex);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }

    private OKResponse sendBroadcastJoinChannelMessage(
            StreamableMessage toSend) {

        OKResponse response = null;

        try {


            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }

    private OKResponse sendChannelMessage(StreamableMessage toSend) {

        OKResponse response = null;

        try {


            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }

    private OKResponse sendBroadcastMessage(StreamableMessage toSend) {

        OKResponse response = null;

        try {


            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
        return response;
    }

    private OKResponse sendExitChannelMessage(StreamableMessage toSend) {

        OKResponse response = null;

        try {


            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }

    private OKResponse sendBroadcastExitChannelMessage(
            StreamableMessage toSend) {

        OKResponse response = null;

        try {


            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }

    private OKResponse sendCloseChannelMessage(StreamableMessage toSend) {

        OKResponse response = null;

        try {


            ObjectOutputStream out = new ObjectOutputStream(
                    toSend.getOutput());
            out.writeObject(toSend.getWrapped());
            out.flush();
            out.close();

            response = new OKResponse();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return response;
    }
}
